﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

/*
mpeg2video:   100Kbits by 00:00:02.2226649, length is 518,0 KBytes
mpeg4     :   100Kbits by 00:00:01.5772743, length is 424,2 KBytes
h264      :   100Kbits by 00:00:04.2703948, length is 117,0 KBytes
mpeg2video:   100Kbits by 00:00:01.5652343, length is 518,0 KBytes
mpeg4     :   100Kbits by 00:00:01.6245419, length is 424,2 KBytes
h264      :   100Kbits by 00:00:04.1360435, length is 117,0 KBytes
mpeg2video:   200Kbits by 00:00:01.5645564, length is 535,8 KBytes
mpeg4     :   200Kbits by 00:00:01.6769727, length is 446,1 KBytes
h264      :   200Kbits by 00:00:04.7858882, length is 228,7 KBytes
mpeg2video:   500Kbits by 00:00:01.5171688, length is 788,6 KBytes
mpeg4     :   500Kbits by 00:00:01.6014096, length is 759,8 KBytes
h264      :   500Kbits by 00:00:06.3999968, length is 568,5 KBytes
mpeg2video:  1500Kbits by 00:00:01.6662206, length is 1806,6 KBytes
mpeg4     :  1500Kbits by 00:00:01.8249712, length is 1807,6 KBytes
h264      :  1500Kbits by 00:00:08.6727333, length is 1708,7 KBytes
mpeg2video:  5000Kbits by 00:00:01.8891667, length is 3113,0 KBytes
mpeg4     :  5000Kbits by 00:00:01.8796463, length is 2534,3 KBytes
h264      :  5000Kbits by 00:00:11.3515441, length is 5790,7 KBytes*/

namespace Universe.MediaProbe.Tests
{
    using System.CodeDom;
    using System.Diagnostics;
    using System.IO;

    using External;

    using NUnit.Framework;

    using Utils;

    using Videos;

    using VSource;

    [TestFixture, Explicit, Category("Perfomance")]
    public class PerfomanceTests
    {
        [TestFixtureSetUp]
        public void TestFixtureSetUp()
        {
            CrossInfo.AttachUnitTrace("MediaProbe Perfomance Tests");
        }

        private static string SourceVideo
        {
            get { return ResManSmall.ShortVideoAsFile.Value; }
        }


        [Test, Explicit, Category("mpeg")]
        public void MPEG()
        {
            Convert_To_Mpeg_Or_Avc("mpeg2video", 100);

            var codecs = new[] { "mpeg4", "mpeg2video", };
            var bitrates = new[] { 100, 200, 500, 1500, 5000 };
            foreach (var br in bitrates)
                foreach (var codec in codecs)
                {
                    Convert_To_Mpeg_Or_Avc(codec, br);
                }

            
        }

        [Test, Explicit, Category("avc")]
        public void MPEG_vs_AVC()
        {
            var codecs = new[] { "mpeg2video", "mpeg4", "h264", };
            // codecs = new[] { "mpeg4", };

            Convert_To_Mpeg_Or_Avc("mpeg2video", 100);

            var bitrates = new[] { 100, 200, 500, 1500, 5000 };
            foreach (var br in bitrates)
            foreach (var codec in codecs)
            {
                Convert_To_Mpeg_Or_Avc(codec, br);
            }
        }

        private static void Convert_To_Mpeg_Or_Avc(string codec, int bitrate)
        {
            string ffMpegExe = ExternalSource.GetFFMpegFile();

                {
                    ConvertOptions ops = new ConvertOptions()
                    {
                        NeedAudioConvert = false,
                        NeedVideoConvert = true,
                        VideoBitRate = bitrate,
                        VideoCodec = codec,
                        Container = ".mkv",
                    };

                    if (codec == "h264")
                        ops.Optional = "-profile:v baseline -level 3.0";

                    else // if (codec == "mpeg2video")
                        ops.Optional = "-g 12";

                    Converter c = new Converter()
                    {
                        Options = ops,
                    };

                    var args = c.GetHumanCommandLine(SourceVideo);
                    string target = SourceVideo + ops.Container;
                    if (File.Exists(target))
                        File.Delete(target);

                    Stopwatch sw = Stopwatch.StartNew();
                    TryConvert(ffMpegExe, args);
                    TimeSpan msecs = sw.Elapsed;
                    var size = CrossInfo.Round(new FileInfo(target).Length/1024m, 1);
                    Trace.WriteLine(string.Format("{0,-10}: {1,5}Kbits by {2}, length is {3:n1} KBytes",
                        codec, bitrate, msecs, size));

                    var t2 = Path.Combine(
                        new FileInfo(target).Directory.FullName,
                        codec + "-" + bitrate.ToString("00000") + "K, " + Path.GetFileName(target));

                    File.Copy(target, t2, true);
                }
        }

        [Test, Explicit, Category("ffv1")]
        public void FFV1()
        {
            string ffMpegExe = ExternalSource.GetFFMpegFile();

            string v1a = @"-level 1 -coder 0 -context 1 -g 20";
            string v1b = @"-level 1 -coder 1 -context 1 -g 20";

            string v3a4 =  "-level 3 -threads 8 -coder 0 -context 1 -g 20 -slices 4  -slicecrc 1";
            string v3a24 = "-level 3 -threads 8 -coder 0 -context 1 -g 20 -slices 24 -slicecrc 1";
            string v3b4 =  "-level 3 -threads 8 -coder 1 -context 1 -g 20 -slices 4  -slicecrc 1";
            string v3b24 = "-level 3 -threads 8 -coder 1 -context 1 -g 20 -slices 24 -slicecrc 1";

            string[] options = new[]
            {
                "v1-Golomb-Rice", v1a,
                "v1-Range-Coder", v1b,
                "v3-Golomb-Rice-04", v3a4,
                "v3-Golomb-Rice-24", v3a24,
                "v3-Range-Coder-04", v3b4,
                "v3-Range-Coder-24", v3b24,
            };

            for (int i = 0; i < options.Length; i+=2)
            {
                string title = options[i], optionalPars = options[i + 1];

                ConvertOptions ops = new ConvertOptions()
                {
                    NeedAudioConvert = false,
                    NeedVideoConvert = true,
                    VideoCodec = "ffv1",
                    Optional = optionalPars,
                    Container = ".mkv",
                };

                Converter c = new Converter()
                {
                    Options = ops,
                };

                var args = c.GetHumanCommandLine(SourceVideo);
                // Trace.WriteLine('"' + ffMpegExe + "\" " + args);
                string target = SourceVideo + ops.Container;
                if (File.Exists(target))
                    File.Delete(target);

                Stopwatch sw = Stopwatch.StartNew();
                TryConvert(ffMpegExe, args);
                TimeSpan msecs = sw.Elapsed;
                var size = CrossInfo.Round(new FileInfo(target).Length / 1024m, 1);
                Trace.WriteLine(string.Format("ffv1 {0,-7}: by {1}, length is {2:n1} KBytes",
                    title, msecs, size));

                var t2 = Path.Combine(
                    new FileInfo(target).Directory.FullName,
                    "ffv1 " + title + ", " + Path.GetFileName(target));

                File.Copy(target, t2, true);

            }

        }

        [TestFixtureTearDown]
        public void TestFixtureTearDown()
        {
            AppDomain.CurrentDomain.DomainUnload += (sender, args) =>
            {
                if (File.Exists(SourceVideo))
                    File.Delete(SourceVideo);
            };
        }

        static void TryConvert(string command, string args)
        {
            string output;
            Exception outputException;
            string error;
            Exception errorException;
            int exitCode;
            CrossInfo.HiddenExec(command, args, out output, out outputException, out error, out errorException, out exitCode);
            if (outputException != null)
                throw new InvalidOperationException("Unable to catch output", outputException);

            if (errorException != null)
                throw new InvalidOperationException("Unable to catch error", errorException);

            // Trace.WriteLine("Exit: " + exitCode);
        }
    }
}
